package com.atguigu.gmall.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

//自定义全局过滤器
@Component
public class LoginGlobalFilter implements GlobalFilter, Ordered {
    //路径匹配工具类
    private AntPathMatcher antPathMatcher = new AntPathMatcher();
    @Autowired
    private RedisTemplate redisTemplate;
    //获取配置文件中的URLs
    @Value("${auth.url}")
    private String[] authUrl;

    public static final String originURL = "http://passport.gmall.com/login.html?originUrl=";
    public static final String USER_LOGIN_KEY_PREFIX = "user:login:";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //获取当前请求路径
        String path = request.getURI().getPath();
        //0、判断请求是否为内部资源
        if (antPathMatcher.match("/**/inner/**", path)) {
            //内部资源 不能直接访问
            //返回错误信息提示 Json格式
            /*抽取成out方法
            Result<Object> result = Result.build(null, ResultCodeEnum.PERMISSION);
            String json = JSONObject.toJSONString(result);
            //创建 DataBuffer,将Json串放进去
            DataBufferFactory dataBufferFactory = response.bufferFactory();
            DataBuffer dataBuffer = dataBufferFactory.wrap(json.getBytes());
            //涉及到读写流，注意乱码问题
            response.getHeaders().set(HttpHeaders.CONTENT_TYPE,
                    MediaType.APPLICATION_JSON_UTF8_VALUE);
            return response.writeWith(Mono.just(dataBuffer));*/
            return outMono(response,ResultCodeEnum.PERMISSION);
        }

        //获取用户ID 单开一个方法
        String userId = getUserId(request);

        //1.1 判断当前请求是否需要登录 同步加异步（异步不能重定向，
        if (antPathMatcher.match("**/auth/**",path) &&
                StringUtils.isEmpty(userId)){
            //未登录，返回错误提示信息 Json格式
            return outMono(response,ResultCodeEnum.LOGIN_AUTH);
        }
        //1.2
        for (String url : authUrl) {//遍历 trade,pay,cart.html等
            if (path.indexOf(url) != -1 && StringUtils.isEmpty(userId)){
                //有，但未登录 重定向至登录页
                try {
                    //重定向的地址设置在响应头中
                    String uri = URLEncoder.encode(request.getURI().getSchemeSpecificPart(),"utf-8");
                    response.getHeaders().set(HttpHeaders.LOCATION, originURL + uri);
                    //设置响应头中的状态码
                    response.setStatusCode(HttpStatus.SEE_OTHER);
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return response.setComplete();
            }
        }
        //3、传递用户ID
        if (!StringUtils.isEmpty(userId)){
            //request.getHeaders().set("userId",userId);
            //会报错，不允许修改请求头
            request.mutate().header("userId",userId);
        }

        //4、传递临时用户ID
        String userTempId = getUserTempId(request);
        if (!StringUtils.isEmpty(userTempId)){
            request.mutate().header("userTempId",userTempId);
        }

        //放行
        return chain.filter(exchange);
    }

    //获取临时用户ID
    private String getUserTempId(ServerHttpRequest request) {
        //1.1获取请求头 有则使用（认为是异步请求
        String userTempId = request.getHeaders().getFirst("userTempId");
        if (StringUtils.isEmpty(userTempId)){
            //1.2请求头中无，直接获取cookie中的 同步请求
            HttpCookie httpCookie = request.getCookies().getFirst("userTempId");
            if (null != httpCookie){
                userTempId = httpCookie.getValue();
            }
        }
        return userTempId;
    }

    //获取用户ID
    private String getUserId(ServerHttpRequest request) {
        //1、获取令牌
        //1.1获取请求头 有则使用（认为是异步请求
        String token = request.getHeaders().getFirst("token");
        if (StringUtils.isEmpty(token)){
            //1.2请求头中无，直接获取cookie中的 同步请求
            HttpCookie httpCookie = request.getCookies().getFirst("token");
            if (null != httpCookie){
                token = httpCookie.getValue();
            }
        }
        //判断是否取到令牌
        if (!StringUtils.isEmpty(token)){
            //2、连接redis获取用户信息(id)
            if (redisTemplate.hasKey(USER_LOGIN_KEY_PREFIX + token)){
                return (String) redisTemplate.opsForValue().get(USER_LOGIN_KEY_PREFIX + token);
            }
        }
        return null;
    }

    //生成Mono对象
    private Mono<Void> outMono(ServerHttpResponse response,ResultCodeEnum resultCodeEnum) {
        Result<Object> result = Result.build(null,resultCodeEnum);
        String json = JSONObject.toJSONString(result);
        //创建 DataBuffer,将Json串放进去
        DataBufferFactory dataBufferFactory = response.bufferFactory();
        DataBuffer dataBuffer = dataBufferFactory.wrap(json.getBytes());
        //涉及到读写流，注意乱码问题
        response.getHeaders().set(HttpHeaders.CONTENT_TYPE,
                MediaType.APPLICATION_JSON_UTF8_VALUE);
        return response.writeWith(Mono.just(dataBuffer));
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
